#include <iostream>
#include <string>
#include <fstream>
#include <vector>
#include <map>
#include <utility>
#include <algorithm>
#include <set>
#include <exception>
#include <sstream>
#include "string_manager.h"


#ifdef _MSC_VER

#include <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp>
using namespace boost::algorithm;
#include <boost/regex.hpp>
#else
#include <unistd.h>
#endif



using namespace std;

typedef std::map<string, string> ScpMap;


string  ConvertWhite2SingleBlank(const char* original_string){
	string temp_string = original_string, final_string, next_token;
	if (temp_string.empty())
	{
		return temp_string;
	}
	istringstream temp_stream(temp_string);
	while ( ! temp_stream.eof())
	{
		temp_stream >> next_token;
		if ( next_token != " " || next_token != "\t")
		{
			final_string = final_string + next_token;
			if (temp_stream.eof()){
				break;
			} else{
				final_string += " ";
			}
		}
	}

	return final_string;
}


string preprocess_string(const char* original_string){
	string temp_string = original_string, processed_string;
	
	if (temp_string.empty())
	{
		return temp_string;
	}
#ifdef _MSC_VER
	boost::regex blank_string("\\s+");
	temp_string = boost::regex_replace(temp_string, blank_string, " ");
	processed_string = boost::trim_copy(temp_string);
#else
	processed_string = ConvertWhite2SingleBlank(temp_string.c_str());
#endif
	
	if (! processed_string.empty())
		return processed_string;
	return temp_string;

}
int FindSpecialPos(const char* original_str, const char* delimiter){
	int pos = -1;
	if (!original_str || !delimiter){
		return pos;
	}
	string deli_array = delimiter, test_str = original_str;
	vector<int> pos_array;
	int i = 0;
	while (i < deli_array.size())
	{
		string::size_type result_pos = test_str.find_first_of(deli_array[i]);
		if (result_pos != string::npos)
		{
			pos_array.push_back(result_pos);
		}
		i++;
	}
	std::vector<int>::iterator min_iter = min_element(pos_array.begin(), pos_array.end());
	if (min_iter != pos_array.end() && (*min_iter) > pos )
	{
		return (*min_iter);
	}
	return pos;
}

int split_line_special(const char* original_string, const char* delimiter, vector<string>* results, int split_times= 1){
	if (!original_string || !delimiter || !results){
		return -1;
	}
	string ori_str = original_string;
	string deli_str = delimiter, part_str = "";
	vector<string> delis;
	string::size_type sub_str_start = 0, sub_str_end = FindSpecialPos(ori_str.c_str(), deli_str.c_str());
	while (sub_str_end != string::npos)
	{
		results->push_back(ori_str.substr(sub_str_start, sub_str_end - sub_str_start));
		part_str = ori_str.substr(sub_str_end+1);
		if (results->size() == split_times)
		{
			results->push_back(part_str);
			break;
		}
		sub_str_start = sub_str_end;
		sub_str_end = FindSpecialPos(part_str.c_str(), deli_str.c_str());
		
	}
	if (results->size()){
		return results->size();
	}
	return -1;
}


int fix_two_scps(const char* scp1, const char* scp2){
	if (! scp1 || ! scp2 || ! CheckFile(scp1) || ! CheckFile(scp2)){
		return -1;
	}
	
	ScpMap refmap, hypmap;
	std::set<string> refset, hypset;
	vector<string> intersect_set;


	ifstream ref_fp(scp1, ios::in);
	if (ref_fp.good()){
		string line;
		vector<string> line_units;
		while (!ref_fp.eof())
		{
			getline(ref_fp, line, '\n');
			line = preprocess_string(line.c_str());
			if (line.empty()) continue;
			split_line_special(line.c_str(), " \t", &line_units, 1);
			if (line_units.size() == 2){
				string scp_key = line_units[0], scp_content = line_units[1];
				ScpMap::const_iterator it = refmap.begin(), end = refmap.end();
				if (refmap.find(scp_key) == end){
					refmap.insert(std::pair<string, string>(scp_key, scp_content));
					refset.insert(scp_key);
				}
			}
			line_units.clear();
		}
	}
	ref_fp.close();
	ifstream hyp_fp(scp2, ios::in);
	if (hyp_fp.good()){
		string line;
		
		vector<string> line_units;
		while (! hyp_fp.eof())
		{
			getline(hyp_fp, line, '\n');
			line = preprocess_string(line.c_str());
			if (line.empty()) continue;
			split_line_special(line.c_str(), " \t", &line_units, 1);
			if (line_units.size() == 2){
				string scp_key = line_units[0], scp_content = line_units[1];
				ScpMap::const_iterator it = hypmap.begin(), end = hypmap.end();
				if (hypmap.find(scp_key) == end){
					hypmap.insert(std::pair<string, string>(scp_key, scp_content));
					hypset.insert(scp_key);
				}
			}
			line_units.clear();
		}
	}
	hyp_fp.close();
	intersect_set.resize(refset.size() + hypset.size());
	vector<string>::iterator retEndPos;
	retEndPos = set_intersection(refset.begin(), refset.end(), hypset.begin(), hypset.end(), intersect_set.begin());
	int interset_size = retEndPos - intersect_set.begin();
	intersect_set.resize(interset_size);
	std::vector<string>::const_iterator iter = intersect_set.begin(), end = intersect_set.end();
	string out_scp1 = scp1, out_scp2=scp2;
	out_scp1.append(".fixed");
	out_scp2.append(".fixed");
	ofstream oscp1(out_scp1.c_str(), ios::out);
	ofstream oscp2(out_scp2.c_str(), ios::out);
	try
	{
		for (; iter != end; iter++)
		{
			oscp1 << (*iter) << " " << refmap[(*iter)] << std::endl;
			oscp2 << (*iter) << " " << hypmap[(*iter)] << std::endl;
		}
	}
	catch (std::exception* e)
	{
		oscp1.close();
		oscp2.close();
		std::cout << e->what() << std::endl;
	}
	oscp1.close();
	oscp2.close();
	return interset_size;
}


int main(int argc, char* argv[]){
	int final_fixed_size = 0;
	if (argc < 2){
		std::cout << argv[0] << " <ref> <hyp1> [<hyp2> <hyp3>...]\n";
		return -1;
	}
	string ref_file_name = argv[1];
	string hyp_file_name = argv[2];
	try
	{
		final_fixed_size = fix_two_scps(ref_file_name.c_str(), hyp_file_name.c_str());
		if (final_fixed_size > 0)
		{
			std::cout << "fix scps successfully !\n";
		}
	}
	catch (exception* e)
	{
		std::cout << e->what() << std::endl;
		std::cout << "fix scps failed\n";
	}
	return 0;
}